package gui;
import javax.swing.*;

import ai.ComputerPlayer;
import ai.MapNavigator;
import ai.Path;
import ai.UnitGroup;


import mapData.*;
import mapData.Map;
import mapObjects.Archer;
import mapObjects.Footman;
import mapObjects.Unit;

import java.awt.*;
import java.lang.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter; 
import java.util.*;

public class GamePanel extends JPanel implements Runnable
{
    private int PWIDTH; //panel width (in pixels)
    private int PHEIGHT; //panel height (in pixels)
    private Thread animator;
    
    private boolean isSelected = false;
    
   
    public static final int TILEW = 7; //tile width 
    public static final int TILEH = 7; //tile height
    
    //public static int tilesAcross, tilesDown;
    
    //selection box coordinates
    //top and bot not necessarily determinant of relative position
    private int topX;
    private int topY;
    private int botX;
    private int botY;
    private boolean drawBox = false;
    
    private boolean b1_pressed;
    private Map gameMap;
    public static AbstractedMap aMap;
    
    private Graphics dbg;
    private Image dbImage = null;
    private volatile boolean running = false;    // stops the animation
    
    private volatile boolean gameOver = false;   // for game termination
    private int winnerID = 99;
    
    private ArrayList<Unit> plyrUnits = new ArrayList<Unit>(0);
    private ArrayList<Unit> compUnits = new ArrayList<Unit>(0);
    
    private ComputerPlayer AI;
    public static MapNavigator pfinder;
    //private Pathfinder globalpf;
    
    private UnitGroup playerSelectionGroup;
    private UnitGroup computerSelectionGroup;
    
    private ArrayList<Unit> player1PathGenQueue = new ArrayList<Unit>(0);
    private ArrayList<Unit> player2PathGenQueue = new ArrayList<Unit>(0);
    
    
    public GamePanel(Map gmMp)
    {
		playerSelectionGroup = new UnitGroup(gmMp.getPlayerColor(), player1PathGenQueue);
		computerSelectionGroup = new UnitGroup(gmMp.getCompColor(), player2PathGenQueue);
	        
		PWIDTH = gmMp.getWidth()*TILEW;
        PHEIGHT = gmMp.getHeight()*TILEH;
        
        setPreferredSize( new Dimension(PWIDTH, PHEIGHT));
        
        topX = 0;
        topY = 0;
        botX = 0;
        botY = 0;
        b1_pressed = false;
        gameMap = gmMp;
        aMap = new AbstractedMap(gameMap, 10, 10);
        
        
        for(int i = 0; i < gameMap.plyrRowList.size(); i++)
        {
            int ID = gameMap.plyrRowList.get(i).getID();
            Color plyrCol = gameMap.plyrRowList.get(i).getRowColor();
            
            while(!gameMap.plyrRowList.get(i).isFinished())
            {
                
                if(ID == 0)
                {
                    plyrUnits.add(new Footman(plyrCol, gameMap.plyrRowList.get(i).getNextUnitPos(), plyrUnits));
                }
                else if(ID == 1)
                {
                    plyrUnits.add(new Archer(plyrCol, gameMap.plyrRowList.get(i).getNextUnitPos(), plyrUnits));
                }
                else
                {
                    System.out.println("No such Unit");
                    System.exit(1);
                }
            }
        }
        System.out.println(plyrUnits.size() + " player Unit(s) added");
        
        for(int i = 0; i < gameMap.cmpRowList.size(); i++)
        {
            int ID = gameMap.cmpRowList.get(i).getID();
            Color cmpCol = gameMap.cmpRowList.get(i).getRowColor();
            
            while(!gameMap.cmpRowList.get(i).isFinished())
            {
                if(ID == 0)
                {
                    compUnits.add(new Footman(cmpCol, gameMap.cmpRowList.get(i).getNextUnitPos(), compUnits));
                }
                else if(ID == 1)
                {
                    compUnits.add(new Archer(cmpCol, gameMap.cmpRowList.get(i).getNextUnitPos(), compUnits));
                }
                else
                {
                    System.out.println("No such Unit");
                    System.exit(1);
                }
            }
        }
        System.out.println(compUnits.size() + " computer Unit(s) added");
        
        Collections.sort(plyrUnits); //needed to ensure binarySearch works
        Collections.sort(compUnits);
        
        pfinder = new MapNavigator(compUnits, plyrUnits, gmMp.obstrList);
        
        //globalpf = new Pathfinder();
        //globalpf.setNavigator(grid);//sets MapNavigator for all classes that extend Pathfinder
        AI = new ComputerPlayer(plyrUnits, compUnits, gameMap.obstrList, computerSelectionGroup);
        
        addMouseListener(new MouseAdapter(){
            public void mousePressed(MouseEvent e){
                if(e.getButton() == 1)//left mouse
                {
                    b1_pressed = true;
                    drawBox = true;
                    playerSelectionGroup.clearGroup();
                    initBox(e.getX(), e.getY());
                }
                else if(e.getButton() == 3)//right mouse button
                {
                	Coord clicked = new Coord(e.getX()/TILEW,e.getY()/TILEH);
                	playerSelectionGroup.setGroupDest(clicked );
                	if(!pfinder.isEmpty(clicked))
                	{
                		Unit occupier = pfinder.getRefToUnit(clicked);
                		if(!occupier.getColor().equals(Map.player)) //if enemy unit
                		{
                			playerSelectionGroup.setGroupTarget(occupier);
                		}
                	}
                    
                    System.out.println("Set destination: "+ clicked);
                    
                    System.out.println("Valid Neighbors" + clicked.getUnobstructedNeighbors());
                }
            }
        });
        
        addMouseMotionListener(new MouseAdapter(){
            public void mouseDragged(MouseEvent e){
                if(b1_pressed)
                    stretchBox(e.getX(), e.getY());
            }
        });
        
        addMouseListener(new MouseAdapter(){
            public void mouseReleased(MouseEvent e){
                b1_pressed = false;
                drawBox = false;
                clearBox();
            }
        });
        
        
        
    }
    
    private void clearBox()
    {
		Coord topLeft = new Coord(Math.min(topX, botX)/TILEW, Math.min(topY, botY)/TILEH);
		int boxTilesAcross = Math.abs(botX-topX)/TILEW;
		int boxTilesDown = Math.abs(topY-botY)/TILEH;
		if(boxTilesAcross == 0)
			boxTilesAcross++;
		if(boxTilesDown == 0)
			boxTilesDown++;
		for(int i = 0; i < boxTilesAcross; i++)
		{
		    for(int j = 0; j < boxTilesDown; j++)
		    {
				Coord pos = new Coord(topLeft.getX()+i, topLeft.getY()+j);
				pfinder.getUnit(pos, playerSelectionGroup);
				
		    }
		}
		System.out.println("Created new selection group of size: " + playerSelectionGroup.groupSize());
		//reset selection box dimensions
	        topX = botX = topY = botY = 0;
    }
    
    private void initBox(int x, int y)
    {
        topX = botX = x;
        topY = botY = y;
        
    }
    
    private void stretchBox(int x2, int y2)
    {
        
        botX = x2;
        botY = y2;
        
    }
    
    public void addNotify( )
        /* Wait for the JPanel to be added to the
         JFrame/JApplet before starting. */
    {
        super.addNotify( );   // creates the peer
        startGame( );         // start the thread
    }
    
    
    private void startGame( )
        // initialise and start the thread
    {
        if (animator == null || !running) {
            animator = new Thread(this);
            animator.start( );
        }
    } // end of startGame( )
    
    public void stopGame( )
        // called by the user to stop execution
    {  running = false;   }
    
    public void run( )
        /* Repeatedly update, render, sleep */
    {
        running = true;
        while(running) {
        	
            gameUpdate( );   // game state is updated
            gameRender( );   // render to a buffer
            repaint( );      // paint with the buffer
            
            
            try {
                animator.sleep(20);  // sleep a bit
            }
            catch(InterruptedException ex){}
        }
        System.exit(0);   // so enclosing JFrame/JApplet exits
    } // end of run( )
    
    private void gameUpdate( )
    { 
        if (!gameOver)
        {
            AI.update();
            moveUnits();
            attackUpdate();
            checkGameOver();
            
        }
    }
    
    public void moveUnits()
    {
    	//remove 3 at a time
    	Unit temp;
    	for(int i= 0; i < 5 ; i++)
    	{
	    	if(player1PathGenQueue.size()>0)
	    	{
	    		temp = player1PathGenQueue.remove(0);
	    		temp.resumeMovement();
	    		temp.forceClearPath();
	    	}
    	}
    	
    	for(int i = 0; i < 5; i++)
    	{
	    	if(player2PathGenQueue.size()>0)
	    	{
	        	temp = player2PathGenQueue.remove(0);
	        	temp.resumeMovement();
	    		temp.forceClearPath();
	    	}
    	}
    	
		for(int i = 0; i < plyrUnits.size(); i++)
		{
		    plyrUnits.get(i).move();
		}
		for(int i = 0; i < compUnits.size(); i++)
		{
		    compUnits.get(i).move();
		}
    }
    
    private void attackUpdate()
    {
    	
    	
    	for(int i = 0; i < plyrUnits.size(); i++)
		{
		    plyrUnits.get(i).attackSetTarget();		    
		}
    	    	
		for(int i = 0; i < compUnits.size(); i++)
		{
		    compUnits.get(i).attackSetTarget();
		   
		}
		
    }
    
    public void checkGameOver()
    {
    	if(compUnits.isEmpty())
    	{
    		gameOver = true;
    		winnerID = 1;
    	}
    	else if(plyrUnits.isEmpty())
    	{
    		gameOver = true;
    		winnerID = 2;
    		
    	}
    }
    
    
    
    private void gameRender( )
        // draw the current frame to an image buffer
    {
        if (dbImage == null){  // create the buffer
            dbImage = createImage(PWIDTH, PHEIGHT);
            if (dbImage == null) {
                System.out.println("dbImage is null");
                return;
            }
            else
                dbg = dbImage.getGraphics( );
        }
        
        // clear the background
        
        dbg.setColor(Color.black);
        dbg.fillRect (0, 0, PWIDTH, PHEIGHT);
        
        //draw selection box
        drawSelectionBox(dbg);
        
        //draw obstructions to terrain
        drawObstructions(dbg);
        
        //draw units
        drawUnits(dbg);
        
        //drawAbstractionGrid(dbg);
        
        //draw selection box for units
        drawSelectionOutline(dbg);
        
        
        //System.out.println(path);
        
        drawUnitsLeft(dbg);
        //draw GameOver message
        if(gameOver)
        {
        	drawGameOverMsg(dbg);        	
        }
 
    }  // end of gameRender( )
    
    private void drawGameOverMsg(Graphics g) 
    {
    	g.setColor(Color.green);
    	g.drawString("Player " + winnerID +" wins!", 0, 10);
		
		
	}

	private void drawObstructions(Graphics g)
    {
        g.setColor(Color.green);
        for(int i = 0; i < gameMap.obstrList.size(); i++)
        {
            g.drawRect(gameMap.obstrList.get(i).getX()*TILEW, gameMap.obstrList.get(i).getY()*TILEH, 
                       gameMap.obstrList.get(i).getWidth()*TILEW, gameMap.obstrList.get(i).getHeight()*TILEH);
        }
    }
    
    private void drawSelectionBox(Graphics g)
    {
    	if(!drawBox)
    		return;
    	dbg.setColor(Color.green);
        dbg.drawLine(topX, topY, botX, topY);
        dbg.drawLine(topX, topY, topX, botY);
        dbg.drawLine(topX, botY, botX, botY);
        dbg.drawLine(botX, topY, botX, botY);
    }
    
    //draw selection outline for selected units
    private void drawSelectionOutline(Graphics g){
    	if(playerSelectionGroup.groupSize()>0){
	    	int x, y;
			g.setColor(new Color(125,199,52));
			for(int i=0;i<playerSelectionGroup.groupSize();i++){
				x = playerSelectionGroup.groupMember(i).getCoord().getX();
				y = playerSelectionGroup.groupMember(i).getCoord().getY();
				g.drawOval(x*TILEW-2,y*TILEH-2,TILEW+4,TILEH+4);
			}
    	}
    }
    
    private void drawUnits(Graphics g)
    {
        //draw player units
        g.setColor(gameMap.getPlayerColor());
        for(int i = 0; i < plyrUnits.size(); i++)
        {
            if(plyrUnits.get(i).getType().equals("footman"))
            {
                g.drawOval(plyrUnits.get(i).getCoord().getX()*TILEW, plyrUnits.get(i).getCoord().getY()*TILEH, TILEW, TILEH);
            }
            else if(plyrUnits.get(i).getType().equals("archer"))
            {
                g.fillOval(plyrUnits.get(i).getCoord().getX()*TILEW, plyrUnits.get(i).getCoord().getY()*TILEH, TILEW, TILEH);
            }
        }
        
        g.setColor(gameMap.getCompColor());
        for(int i = 0; i < compUnits.size(); i++)
        {
            if(compUnits.get(i).getType().equals("footman"))
            {
                g.drawOval(compUnits.get(i).getCoord().getX()*TILEW, compUnits.get(i).getCoord().getY()*TILEH, TILEW, TILEH);
            }
            else if(compUnits.get(i).getType().equals("archer"))
            {
                g.fillOval(compUnits.get(i).getCoord().getX()*TILEW, compUnits.get(i).getCoord().getY()*TILEH, TILEW, TILEH);
            }
        }
    }
    
    public void drawGrid(Graphics g) //for debugging purposes only
    {
    	for(int i = 0; i < Map.width; i++)
    	{
    		for(int j = 0; j < Map.height; j++)
    		{
    			g.drawRect(i*TILEW, j*TILEH, TILEW, TILEH);
    		}
    	}
    	
    }
    
    public void drawUnitsLeft(Graphics g)
    {
    	g.setColor(Color.WHITE);
    	g.drawString(new String("Player 1 units left: "+plyrUnits.size()), PWIDTH - 130, 10);
    	g.drawString(new String("Player 2 units left: "+compUnits.size()), PWIDTH - 130, 22);
    }
    
    public void drawAbstractionGrid(Graphics g) //for debugging purposes only
    {
    	g.setColor(Color.WHITE);
    	ArrayList<CoordCluster> list = aMap.listAll();
    	int size = list.size();
    	for(int i = 0; i < size; i++)
    	{
    		
    		g.drawRect(list.get(i).getTopLeft().getX()*TILEW, list.get(i).getTopLeft().getY()*TILEH, 
    				TILEW*(list.get(i).getBottomRight().getX() - list.get(i).getTopLeft().getX())+TILEW,
    				TILEH*(list.get(i).getBottomRight().getY() - list.get(i).getTopLeft().getY())+TILEH
    				);
    	}
    	
    }
    
    
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (dbImage != null)
            g.drawImage(dbImage, 0, 0, null);
        
    }
}





